//====================================================================================================
#include "CField.h"
#include "CApp.h"

#include <stdlib.h>
#include <ctime>
#include <vector>

using namespace std;

const int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

CField CField::Field;

//====================================================================================================
CField::CField() {
    GameOver = false;
    Score = 0;
    TMP_Score = 0;
    Live = false;
    MotionX = MotionY = Animate = false;
    Focus.x = 0; Focus.y = 0;
}

//====================================================================================================
CField::~CField() {
}

//====================================================================================================
bool CField::OnInit(int Wx, int Hx) {
    Width = Wx / 40;
    Height = Hx / 40 - 1;

    if (!Live) {
        srand(time(NULL));
        for (int i = 0; i < Width; i++)
            for (int j = 0; j < Height; j++)
                Map[i][j].OnInit(i, j);
        Live = true;
    }

    return true;
}

//====================================================================================================
void CField::OnRender(SDL_Surface* Surf_Display, SDL_Surface* image) {
    for (int i = 0; i < Width; i++)
        for (int j = 0; j < Height; j++)
            Map[i][j].OnRender(Surf_Display, image, Map[i][j].color);
}

//====================================================================================================
bool CField::OkField(int x, int y) {
    return (Map[x][y].Live == true && x >= 0 && x < Width && y >= 0 && y < Height);
}

//====================================================================================================
void CField::Check() {
    // Проверка по Y
    for (int i = 0; i < Width; i++)
        for (int j = 1; j < Height; j++)
            if (!Map[i][j].Live) {
                for (int k = j; k > 0; k--) {
                    Map[i][k] = Map[i][k - 1];
                }
                Map[i][0].Live = false;
                MotionY = true;
            }

    // Проверка по X
    for (int k = Width - 1; k >= 0; k--)
        if (!Map[k][Height - 1].Live) {
            for (int i = k; i < Width - 1; i++)
                for (int j = 0; j < Height; j++)
                    Map[i][j] = Map[i + 1][j];
                for (int  j = 0; j < Height; j++)
                    Map[Width - 1][j].Live = false;
            MotionX = true;
        }

    int tmp = 0;
    if (MotionX)
        for (int i = 0; i < Width; i++)
            if (!Map[i][Height - 1].Live) {
                for (int j = 0; j < Height; j++)
                    Map[i][j].OnInit(Width + tmp, j);
                tmp++;
            }

    if (MotionY)
        for (int i = 0; i < Width; i++)
            V[i] = rand() % 4 + 2;

    // Проверка на конце игры
    for (int i = 0; i < Width; i++)
        for (int j = 0; j < Height; j++)
            for (int k = 0; k < 4; k++) {
                int tx = i + dir[k][0]; int ty = j + dir[k][1];
                if (OkField(tx, ty) && Map[i][j].Live && Map[tx][ty].color == Map[i][j].color)
                     return;
            }

    Live = false;
}

//====================================================================================================
void CField::ClearFocus() {
    Map[Focus.x][Focus.y].Focus = false;
}

//====================================================================================================
void CField::ClearSelect() {
    for (int i = 0; i < int(SelectedBalls.size()); i++)
        Map[SelectedBalls[i].x][SelectedBalls[i].y].Select = false;
    SelectedBalls.clear();
}

//====================================================================================================
void CField::KillBalls() {
    int sum = 0;
    for (int i = 0; i < Width; i++)
        for (int j = 0; j < Height; j++)
            if (Map[i][j].Select) {
                //Map[i][j].Live = false;
                sum++;
            }
    Buf_Score += sum * (sum - 1);
}

//====================================================================================================
void CField::FocusBall(int x, int y) {
    ClearFocus();
    Map[x][y].Focus = true;
    Focus.x = x; Focus.y = y;
}

//====================================================================================================

void CField::DFS(int x, int y, int color) {
    Map[x][y].Select = true;
    TMP_Score++;
    xoy tmp;
    tmp.x = x; tmp.y = y;
    SelectedBalls.push_back(tmp);

    for (int i = 0; i < 4; i++) {
        int tx = x + dir[i][0]; int ty = y + dir[i][1];
        if (OkField(tx, ty) && Map[tx][ty].color == color && Map[tx][ty].Select == false)
            CField::DFS(tx, ty, color);
    }
}

//====================================================================================================
bool CField::SelectBall(int x, int y) {
    TMP_Score = 0;
    if (Map[x][y].Select && !MotionX && !MotionY) {
        KillBalls();
        ClearSelect();
        Map[Focus.x][Focus.y].Animate = true;
        Animate = true;
        ClearFocus();
        //Check();
        return true;
    } else {
        ClearSelect();
        DFS(x, y, Map[x][y].color);
        TMP_Score = TMP_Score * (TMP_Score - 1);
        if (!SelectedBalls.empty() && SelectedBalls.size() < 2)
            ClearSelect();
        else
            ClearFocus();
    }
    return false;
}

void CField::OnLoop() {
    if (MotionY) {
        MotionY = false;

        for (int i = 0; i < Width; i++)
            for (int j = 0; j < Height; j++)
                if (Map[i][j].Live && Map[i][j].Y != j * Map[i][j].Height && (j == Height - 1 || Map[i][j + 1].Y - Map[i][j].Y > 40)) {
                    Map[i][j].vy += 0.5 / V[i];
                    Map[i][j].Y += Map[i][j].vy;
                    MotionY = true;
                    if (Map[i][j].Y >= j * Map[i][j].Height) {
                        Map[i][j].Y = j * Map[i][j].Height;
                        Map[i][j].vy = 0;
                    }
                }
        return;
    }

    if (!MotionY && MotionX) {
        MotionX = false;
        for (int i = 0; i < Width; i++)
            if (Map[i][Height - 1].Live && Map[i][Height - 1].X != i * Map[i][Height - 1].Width && ( i == 0 || i == Width - 1 || Map[i][Height - 1].X - Map[i - 1][Height - 1].X > 39))
                for (int j = 0; j < Height; j++) {
                    Map[i][j].vx += 1;
                    Map[i][j].X -= Map[i][j].vx;
                    MotionX = true;
                    if (Map[i][j].X <= i * Map[i][j].Width) {
                        Map[i][j].X = i * Map[i][j].Width;
                        Map[i][j].vx = 0;
                    }
                }

    }
}

void CField::OnAnimate() {
    if(OldTime + 40 > int(SDL_GetTicks())) {
        return;
    }

    vector<xoy> bData;

    Field.Animate = false;
    for (int i = 0; i < Width; i++)
        for (int j = 0; j < Height; j++)
            if (Map[i][j].Animate) {
                Map[i][j].OnAnimate();
                Field.Animate = true;
                xoy buf;
                buf.x = i; buf.y = j;
                bData.push_back(buf);
            }

    for (int i = 0; i < int(bData.size()); i++)
        for (int k = 0; k < 4; k++)
            if (Map[bData[i].x][bData[i].y].Animate){
                xoy tmp;
                tmp.x = bData[i].x + dir[k][0]; tmp.y = bData[i].y + dir[k][1];
                if (OkField(tmp.x, tmp.y) && !Map[tmp.x][tmp.y].Animate && Map[bData[i].x][bData[i].y].color == Map[tmp.x][tmp.y].color)
                    Map[tmp.x][tmp.y].SetAnimate();
            }

    if (!Field.Animate)
        Check();

    OldTime = SDL_GetTicks();
}
